using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Security.Principal;
using System.Text;
using System.Xml.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.WebControls;

using System.Collections;

namespace PhilWicklund.com.VirtualEarthWebPart
{
	/// <summary>
	/// Description for MicrosoftVirtualMapWP.
	/// </summary>
	//[DefaultProperty("Text"),
	//ToolboxData("<{0}:VirtualEarthWebPart runat=server></{0}:VirtualEarthWebPart>"),
	//XmlRoot(Namespace = "PhilWicklund.com.VirtualEarthWebPart")]

	public class VirtualEarthWebPart : System.Web.UI.WebControls.WebParts.WebPart
	{
		private bool showMap = false;
		private int mapWidth = 695;
		private int mapHeight = 800;
        private int findDelay = 750;
		private string errorString = "";

		[System.Web.UI.WebControls.WebParts.WebBrowsable(true),
		 System.Web.UI.WebControls.WebParts.WebDisplayName("Show Map"),
		 System.Web.UI.WebControls.WebParts.WebDescription("Show Map - true | Show Addresses - false"),
		 System.Web.UI.WebControls.WebParts.Personalizable(
		 System.Web.UI.WebControls.WebParts.PersonalizationScope.User),
		 System.ComponentModel.Category("Map Specifications"),
		 System.ComponentModel.DefaultValue(false)
		]
		public bool ShowMap
		{
			get
			{
				return showMap;
			}

			set
			{
				showMap = value;
			}
		}

		[System.Web.UI.WebControls.WebParts.WebBrowsable(true),
		 System.Web.UI.WebControls.WebParts.WebDisplayName("Map Width in Pixles"),
		 System.Web.UI.WebControls.WebParts.WebDescription("Map Width in Pixles"),
		 System.Web.UI.WebControls.WebParts.Personalizable(
		 System.Web.UI.WebControls.WebParts.PersonalizationScope.User),
		 System.ComponentModel.Category("Map Specifications"),
		 System.ComponentModel.DefaultValue(695)
		]
		public int MapWidth
		{
			get
			{
				return mapWidth;
			}

			set
			{
				mapWidth = value;
			}
		}

		[System.Web.UI.WebControls.WebParts.WebBrowsable(true),
		 System.Web.UI.WebControls.WebParts.WebDisplayName("Map Height in Pixles"),
		 System.Web.UI.WebControls.WebParts.WebDescription("Map Height in Pixles"),
		 System.Web.UI.WebControls.WebParts.Personalizable(
		 System.Web.UI.WebControls.WebParts.PersonalizationScope.User),
		 System.ComponentModel.Category("Map Specifications"),
		 System.ComponentModel.DefaultValue(800)
		]
		public int MapHeight
		{
			get
			{
				return mapHeight;
			}

			set
			{
				mapHeight = value;
			}
		}

        [System.Web.UI.WebControls.WebParts.WebBrowsable(true),
         System.Web.UI.WebControls.WebParts.WebDisplayName("Find Delay"),
         System.Web.UI.WebControls.WebParts.WebDescription("Specify time to wait between search results - allows map to finish moving before it draws the pin point."),
         System.Web.UI.WebControls.WebParts.Personalizable(
         System.Web.UI.WebControls.WebParts.PersonalizationScope.User),
         System.ComponentModel.Category("Map Specifications"),
         System.ComponentModel.DefaultValue(1000)
        ]
        public int FindDelay
        {
            get
            {
                return findDelay;
            }

            set
            {
                findDelay = value;
            }
        }

		protected override void OnLoad(EventArgs e)
		{
			try
			{
                System.Web.UI.ClientScriptManager csm = Page.ClientScript;
                StringBuilder builder = new StringBuilder();
                string SCRIPT_NAME = "MapScript" + this.ID;
                
                if (ShowMap == true && !this.WebPartManager.DisplayMode.AllowPageDesign)
                {
                    SPWeb web = SPControl.GetContextWeb(Context);

                    SPList addressList = web.Lists["Microsoft Virtual Earth Addresses"];
                    
                    //General JavaScript functions that can be added by any Web Part
                    
                    builder.Append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"> \n");
                    builder.Append("<script type=\"text/javascript\" src=\"http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2\"></script> \n");
                    builder.Append("<script type=\"text/javascript\"> \n");
                    builder.Append("    var map = null; \n");
                    builder.Append("    var pins = new Array(); \n");
                    builder.Append("    var locations = new Array(); \n");
                    builder.Append("    var titles = new Array(); \n");
                    builder.Append("    var metadata = new Array(); var locationCount = 0;\n");

                    //This call generates the need javascript for the locations stored
                    //in the SharePoint list
                    builder.Append(GetLocationsArrayJavascript(addressList));
                    builder.Append("    function GetMap() \n");
                    builder.Append("    { \n");
                    builder.Append("        try  \n");
                    builder.Append("        { \n");
                    builder.Append("            map = new VEMap('myMap');\n");
                    builder.Append("            map.LoadMap(); \n");
                    builder.Append("            AddLocations(); \n");
                    builder.Append("		} \n");
                    builder.Append("		catch (e) \n");
                    builder.Append("	    { \n");
                    builder.Append("		    alert('GetMap: ' + e.message); \n");
                    builder.Append("	    } \n");
                    builder.Append("    } \n");

                    builder.Append("    function AddLocations() \n");
                    builder.Append("    { \n");
                    builder.Append("        try  \n");
                    builder.Append("        { \n");
                    builder.Append("            map.Find(null, locations[locationCount], \n");
                    builder.Append("                null, null, null, null, null, null,false, true, \n");
                    builder.Append("            function() { \n");
                    builder.Append("                setTimeout('afterdelay(' + locationCount++ + ')', " + findDelay.ToString() + ");\n");
                    builder.Append("            }); \n");
                    builder.Append("        } catch (e) { \n");
                    builder.Append("            alert('Add Locations: ' + e.message); \n");
                    builder.Append("        } \n");
                    builder.Append("      }      \n");

                    builder.Append("    function afterdelay(count){ \n");
                    builder.Append("                var pPin = new VEShape(VEShapeType.Pushpin, map.GetCenter()); \n");
                    builder.Append("                pPin.SetTitle(titles[count]); \n");
                    builder.Append("                pPin.SetDescription(locations[count] + metadata[count]); \n");
                    builder.Append("                pins[count] = pPin; \n");
                    builder.Append("                if (count < locations.length - 1) { \n");
                    builder.Append("                    AddLocations(count); }\n");
                    builder.Append("                else { map.AddShape(pins); map.SetMapView(pins); \n");
                    builder.Append("                    document.getElementById('myMap').style.visibility = 'visible'; \n");
                    builder.Append("                } \n}\n");
                }
                else
                {
                    builder.Append("<script type=\"text/javascript\"> \n");
                    builder.Append("function toggleDiv(divId, imageId) \n");
                    builder.Append("{ \n");
                    builder.Append("    var element = document.getElementById(divId); \n");
                    builder.Append("    if (element.style.display == 'block') \n");
                    builder.Append("    { \n");
                    builder.Append("        element.style.display = 'none'; \n");
                    builder.Append("        imageId.src = '/_layouts/images/plus.gif'; \n");
                    builder.Append("} \n");
                    builder.Append("else \n");
                    builder.Append("{ \n");
                    builder.Append("    element.style.display = 'block'; \n");
                    builder.Append("    imageId.src = '/_layouts/images/minus.gif'; \n");
                    builder.Append("} \n");
                    builder.Append("} \n");
                }
                
                builder.Append("_spBodyOnLoadFunctionNames.push('GetMap'); \n</script> \n");
                csm.RegisterClientScriptBlock(
                        this.GetType(),
                        SCRIPT_NAME,
                        builder.ToString(),
                        false);
			}
			catch (Exception exe)
			{
				errorString = exe.Message;
			}
			
		}

		protected override void CreateChildControls()
		{
			try
			{
				if (this.errorString != "")
					throw new Exception(errorString);

				base.CreateChildControls();

				SPWeb web = SPControl.GetContextWeb(Context);
				SPList list;

				try
				{
                    list = web.Lists["Microsoft Virtual Earth Addresses"];
				}
				catch (Exception)
				{
					// If the "Microsoft Virtual Map Locations" list doesn't exist on
					// this web, render the build list button and prompt
					BuildCreateNewListOption();
					return;
				}

				// If there are no locations in the SP list, prompt user to add some
				if (list.Items.Count == 0)
				{
                    this.Controls.Add(new LiteralControl("Microsoft Virtual Earth Addresses is empty. Please <a target='_blank' href='" + web.Url + "/Lists/Microsoft%20Virtual%20Earth%20Addresses/NewForm.aspx'>add</a> some locations to plot on this map."));
					return;
				}

				if (ShowMap == true)
				{
					if (this.WebPartManager.DisplayMode.AllowPageDesign)
					{
						this.Controls.Add(new LiteralControl("Map will not show while in design mode."));
					}
					else
					{
						StringBuilder builder = new StringBuilder();
						builder.Append("<div id='myMap' style='position: relative; width: " + MapWidth + "px; height: " + MapHeight + "px;'></div>");
						this.Controls.Add(new LiteralControl(builder.ToString()));
					}
				}
				else
					BuildAddressList(list);

			}
			catch (Exception e)
			{
				this.Controls.Add(new LiteralControl("ERROR: " + e.Message));
			}
		}

		private void BuildCreateNewListOption()
		{
			try
			{
				LinkButton newList = new LinkButton();
				newList.Text = "Create a new one";
				newList.Click += new EventHandler(newList_Click);

				this.Controls.Add(new LiteralControl("No Microsoft Virtual Earth address list exists on this site. "));
				this.Controls.Add(newList);
				this.Controls.Add(new LiteralControl(" and add the addresses you want to be populated on the map."));
			} catch (Exception e)
			{
				this.Controls.Add(new LiteralControl(e.Message));
			}
		}

		private void newList_Click(object sender, System.EventArgs e)
		{
			SPWeb web = SPControl.GetContextWeb(Context);
			web.AllowUnsafeUpdates = true;

			//TODO: catch security exception if running user doesn't have rights to create a list
			//or run with elevated privelidges
            web.Lists.Add("Microsoft Virtual Earth Addresses", "Address store for all the pin-points used by the MVE webpart to place pins on the map. Follow instructions to ensure accuracy.", SPListTemplateType.GenericList);

            SPList addressList = web.Lists["Microsoft Virtual Earth Addresses"];
			SPView view = addressList.Views["All Items"];
			SPViewFieldCollection viewFields = view.ViewFields;

			SPField field = addressList.Fields["Title"];
			field.Title = "Title";
			field.Description = "Enter a descriptive title for this address, e.g. 'Minneapolis Office'.";
			field.Update();

			string fieldID = addressList.Fields.Add("Address1", SPFieldType.Text, true);
			field = (SPField)addressList.Fields.GetField(fieldID);
			field.Description = "Use abbreviations, e.g. 'Second Avenue' needs to be '2nd Ave'.";
			field.Update();
			viewFields.Add(field);
			view.Update();

			fieldID = addressList.Fields.Add("Address2", SPFieldType.Text, false);
			field = (SPField)addressList.Fields.GetField(fieldID);
			field.Description = "Suite, floor, room, building, unit, office, office, apartment number";
			viewFields.Add(field);
			view.Update();

			fieldID = addressList.Fields.Add("City", SPFieldType.Text, true);
			field = (SPField)addressList.Fields.GetField(fieldID);
			viewFields.Add(field);
			view.Update();

			fieldID = addressList.Fields.Add("State", SPFieldType.Text, true);
			field = (SPField)addressList.Fields.GetField(fieldID);
			field.Description = "Enter a two-character state ID (ex. MN, AZ, NY, etc.)";
			field.Update();
			viewFields.Add(field);
			view.Update();

			fieldID = addressList.Fields.Add("Zip Code", SPFieldType.Text, false);
			field = (SPField)addressList.Fields.GetField(fieldID);
			viewFields.Add(field);

			fieldID = addressList.Fields.Add("URL", SPFieldType.Text, false);
			field = (SPField)addressList.Fields.GetField(fieldID);
			field.Description = "Provide an associated URL, if one exists.";
			field.Update();

			fieldID = addressList.Fields.Add("Phone", SPFieldType.Text, false);
			field = (SPField)addressList.Fields.GetField(fieldID);
			field.Description = "Provide a phone # associated with this location, if one exists.";
			field.Update();
			
			fieldID = addressList.Fields.Add("Fax", SPFieldType.Text, false);
			field = (SPField)addressList.Fields.GetField(fieldID);
			field.Description = "Provide a fax # associated with this location, if one exists.";
			field.Update();


			Page.Response.Redirect(web.Url + "/Lists/Microsoft%20Virtual%20Earth%20Addresses");

		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns> Returns javascript much like the following:
		///           locations[0] = \"23421 Texas Ave S, St Louis, AZ 76443\";
		///           locations[1] = \"23421 Texas Ave S, St Louis, AZ 76443\";
		///           locations[2] = \"23421 Texas Ave S, St Louis, AZ 76443\";
		/// </returns>
		private string GetLocationsArrayJavascript(SPList addressList)
		{
			int locations = 0;
			StringBuilder locationsJavaScript = new StringBuilder();

			foreach(SPListItem item in addressList.Items)
			{
                string name = item["Title"].ToString();
                if (name.IndexOf(".") != -1)
                    name = RemoveSensitiveChars(name.Remove(name.IndexOf(".")));
                else
                    name = RemoveSensitiveChars(name);
                string address1 = item["Address1"].ToString();
                //string address2 = item["Address2"].ToString();
                string city = item["City"].ToString();
                string state = item["State"].ToString();
                string zip = item["Zip Code"].ToString();
                string url = ""; try{ url = item["URL"].ToString(); } catch (NullReferenceException) {}
                string phone = ""; try{  phone = item["Phone"].ToString(); } catch (NullReferenceException) {}
                string fax = ""; try{  fax = item["Fax"].ToString(); } catch (NullReferenceException) {}


                locationsJavaScript.Append("           titles[" + locations + "] = \"");
                if (url != "")
                    locationsJavaScript.Append("<a href='" + url + "'>" + name + "</a>");
                else
                    locationsJavaScript.Append(name);
                locationsJavaScript.Append("\";");

				locationsJavaScript.Append("           locations[" + locations + "] = \"");
				locationsJavaScript.Append(GetComplientAddress(address1) + ", " + RemoveSensitiveChars(city) + ", " + RemoveSensitiveChars(state) + " " + RemoveSensitiveChars(zip));
				locationsJavaScript.Append("\";");

                locationsJavaScript.Append("           metadata[" + locations + "] = \"");
                if (phone != "")
                    locationsJavaScript.Append("<br />Phone: " + RemoveSensitiveChars(phone));
                if (fax != "")
                    locationsJavaScript.Append("<br />Fax: " + RemoveSensitiveChars(fax));
                locationsJavaScript.Append("\";");
				locations++;
			}
            return locationsJavaScript.ToString();
		}

		private string RemoveSensitiveChars(string s)
		{
			return s.
				Replace("'", "").
				Replace("\"", "");
		}

		private string GetComplientAddress(string address)
		{
			return RemoveSensitiveChars(address).
				Replace("#", "No").
				Replace("NORTH WEST", "NW").
				Replace("NORTH EAST", "NE").
				Replace("SOUTH WEST", "SW").
				Replace("SOUTH EAST", "SE").
				Replace("NORTH", "N").
				Replace("SOUTH", "S").
				Replace("EAST", "E").
				Replace("WEST", "W").
				Replace("HWY", "HIGHWAY").
				Replace("US HIGHWAY", "US HWY").
				Replace("BOULEVARD", "BLVD").
				Replace("LANE", "LN").
				Replace("CENTER", "CTR").
				Replace("CIRCLE", "CIR").
				Replace("MOUNTAIN", "MTN").
				Replace("MOUNT", "MT").
				Replace("PARKWAY", "PKWY").
				Replace("PLAZA", "PLZ").
				Replace("POINT", "PT").
				Replace("ROUTE", "RTE").
				Replace("SQUARE", "SQ").
				Replace("TERRACE", "TER").
				Replace("DRIVE", "DR").
				Replace("STREET", "ST").
				Replace("COURT", "CT").
				Replace("ROAD", "RD").
				Replace("AVENUE", "AVE").
				Replace(".", "");
		}
         
		private void BuildAddressList(SPList list)
		{
			StringBuilder addressCode = new StringBuilder();

			Hashtable locations = new Hashtable();
			ArrayList sort = new ArrayList();

			addressCode.Append("<div style='padding-left:3px'><table>");
			foreach (SPListItem item in list.Items)
			{
				StringBuilder locationHtml = new StringBuilder();
				bool addressPresent = false;
				try
				{
					string address = item["Address1"].ToString();
					addressPresent = true;
				}
				catch (NullReferenceException) { }

                string uniqueID = Guid.NewGuid().ToString().Replace("-", "");
				if (uniqueID == "" || uniqueID == null)
					continue;
				locationHtml.Append("<tr><td valign='top'>");
				try
				{
					string url = item["URL"].ToString();
					if (addressPresent)
						locationHtml.Append("<img id=\"img" + uniqueID + "\" src=\"/_layouts/images/plus.gif\" onclick=\"javascript:toggleDiv('" + uniqueID + "', this);\" /></td><td><a href='" + url + "'>" + item["Title"].ToString() + "</a><br>");
					else
						locationHtml.Append("-</td><td><a href='" + url + "'>" + item["Title"].ToString() + "</a><br>");
				}
				catch (NullReferenceException)
				{
					if (addressPresent)
						locationHtml.Append("<img id=\"img" + uniqueID + "\" src=\"/_layouts/images/plus.gif\" onclick=\"javascript:toggleDiv('" + uniqueID + "', this);\" /></td><td>" + item["Title"].ToString() + "<br>");
					else
						locationHtml.Append("-</td><td>" + item["Title"].ToString() + "<br>");
				}

				locationHtml.Append("<div style='padding-left:7px' id=\"" + uniqueID + "\" style=\"display:none;\">");
				try
				{
					locationHtml.Append(item["Address1"].ToString() + "<br>");
				}
				catch (NullReferenceException) { }

				try
				{
					string address2 = item["Address2"].ToString();
					locationHtml.Append(address2 + "<br>");
				}
				catch (NullReferenceException) { }
				try
				{
					locationHtml.Append(item["City"].ToString() + ", ");
				}
				catch (NullReferenceException) { }
				try
				{
					locationHtml.Append(item["State"].ToString() + " ");
				}
				catch (NullReferenceException) { }

				try
				{
					locationHtml.Append(item["Zip Code"].ToString() + "<br>");
				}
				catch (NullReferenceException) { }

				try
				{
					string phone = item["Phone"].ToString();
					locationHtml.Append("P " + phone + "<br>");
				}
				catch (NullReferenceException) { }

				try
				{
					string fax = item["Fax"].ToString();
					locationHtml.Append("F " + fax + "<br>");
				}
				catch (NullReferenceException) { }


				locationHtml.Append("</div></td></tr>");
				locations.Add(uniqueID, locationHtml.ToString());
				sort.Add(uniqueID);
			}
			sort.Sort();
			foreach (string key in sort)
			{
				addressCode.Append(locations[key].ToString());
			}
			addressCode.Append("</table></div>");
			this.Controls.Add(new LiteralControl(addressCode.ToString()));
		}
	}
}
